#ifndef MN_ASSET_MANAGER_
#define MN_ASSET_MANAGER_

//TODO: add time stamps to log enteries: 
//http://www.codersource.net/cpp_date_time.html

#include <stdio.h>
#include <string>
#include <list>
#include <fstream>
#include <iostream>

#define ASSETLOG
//Comment this out to disable logging of asset loading/unloading.



template< typename T >
struct AssetHolder {
  T *assetPtr;
  std::string filename;
  unsigned int ID;
};

template< typename T >
class AssetManager
{
 public:
  AssetManager(const char *logFile);
  virtual ~AssetManager();

  T* checkLoaded(char *filename);
  T* Aquire(char *filename, unsigned int &uid, void *userData = NULL);
  T* Aquire(char *filename, void *userData = NULL);
  T* isIDLoaded(unsigned int ID);
  bool DeleteByID(unsigned int ID);
  bool DeleteByPtr(T* ptr);
  bool DeleteByFilename(char *filename);
 protected:
  unsigned int uIDCount;
  virtual T* Load(char *filename, unsigned int &userID, 
		  void *userData = NULL) = 0;
  virtual T* Load(const char *filename, unsigned int &userID, 
		  void *userData = NULL) = 0;

#ifdef ASSETLOG
  std::string logFileName;
  std::ofstream logFile;
#endif

 private:
  std::list< AssetHolder<T> > assetList;
};


/////////////IMPLEMENTATION////////////


//Constructor
template< typename T > 
AssetManager<T>::AssetManager(const char *logFile)
{
  uIDCount = 0;
#ifdef ASSETLOG
  logFileName = logFile;
  AssetManager::logFile.open(logFile);
#endif
}

template< typename T > 
T* AssetManager<T>::isIDLoaded(unsigned int ID)
{
  typename std::list< AssetHolder<T> >::iterator iter = assetList.begin();
  while(iter!=assetList.end())
    {
      if(iter->ID==ID)
	return iter->assetPtr;
      iter++;
    }
  return NULL;
}

//Destructor
template< typename T > 
AssetManager<T>::~AssetManager()
{
  typename std::list< AssetHolder<T> >::iterator iter = assetList.begin();
  while(iter!=assetList.end())
    {
      delete iter->assetPtr;
#ifdef ASSETLOG
      logFile<<"II - Unloaded "<<iter->filename.c_str()<<"\n";
      logFile.flush();
#endif
      iter++;
    }
#ifdef ASSETLOG
  logFile.close();
#endif
}

//The checkLoaded function
template< typename T > 
T* AssetManager<T>::checkLoaded(char *filename)
{
  typename std::list< AssetHolder<T> >::iterator iter = assetList.begin();
  while(iter!=assetList.end())
    {
      if(strcmp(iter->filename.c_str(), filename)==0)
	return iter->assetPtr;
      iter++;
    }
  return NULL;
}

template< typename T > 
T* AssetManager<T>::Aquire(char *filename, unsigned int &uid, void *userData)
{
  T* check = checkLoaded(filename);
  if(check==NULL)
    {
      unsigned int userID;
      T *loadedPtr = Load(filename, userID, userData);
      if(loadedPtr!=NULL)
	{
	  AssetHolder<T> newAssetHolder;
	  newAssetHolder.assetPtr = loadedPtr;
	  newAssetHolder.filename = filename;
	  newAssetHolder.ID = userID;
	  uid = userID;
	  assetList.push_back(newAssetHolder);
#ifdef ASSETLOG
	  logFile<<"II - Loaded "<<filename<<"\n";
	  logFile.flush();
#endif
	  return loadedPtr;
	} else
	{
#ifdef ASSETLOG
	  logFile<<"EE - Failed to load "<<filename<<"\n";
	  logFile.flush();
#endif
	  return NULL;
	}
    } else
    return check;
}

template< typename T > 
T* AssetManager<T>::Aquire(char *filename, void *userData)
{
  T* check = checkLoaded(filename);
  if(check==NULL)
    {
      unsigned int userID;
      T *loadedPtr = Load(filename, userID, userData);
      if(loadedPtr!=NULL)
	{
	  AssetHolder<T> newAssetHolder;
	  newAssetHolder.assetPtr = loadedPtr;
	  newAssetHolder.filename = filename;
	  newAssetHolder.ID = userID;
	  assetList.push_back(newAssetHolder);
#ifdef ASSETLOG
	  logFile<<"II - Loaded "<<filename<<"\n";
	  logFile.flush();
#endif
	  return loadedPtr;
	} else
	{
#ifdef ASSETLOG
	  logFile<<"EE - Failed to load "<<filename<<"\n";
	  logFile.flush();
#endif
	  return NULL;
	}
    } else
    return check;
}

template< typename T > 
bool AssetManager<T>::DeleteByID(unsigned int ID)
{
  typename std::list< AssetHolder<T> >::iterator iter = assetList.begin();
  while(iter!=assetList.end())
    {
      if(iter->ID == ID)
	{
#ifdef ASSETLOG
	  logFile<<"II - Unloaded "<<iter->filename<<" by ID. ID="
		 <<ID<<std::endl;
	  logFile.flush();
#endif
	  delete iter->assetPtr;
	  assetList.erase(iter);
	  return true;
	}
      iter++;
    }
#ifdef ASSETLOG
  logFile<<"WW - Tried to unload an object by ID, but the object doesn't exist.\
 ID="<<ID<<std::endl;
  logFile.flush();
#endif
  return false;
}

template< typename T > 
bool AssetManager<T>::DeleteByPtr(T* ptr)
{
  typename std::list< AssetHolder<T> >::iterator iter = assetList.begin();
  while(iter!=assetList.end())
    {
      if(iter->assetPtr == ptr)
	{
#ifdef ASSETLOG
	  logFile<<"II - Unloaded "<<iter->filename<<" by pointer."<<std::endl;
	  logFile.flush();
#endif
	  delete iter->assetPtr;
	  assetList.erase(iter);
	  return true;
	}
      iter++;
    }
#ifdef ASSETLOG
  logFile<<"WW - Tried to unload an object by pointer, but the object doesn't\
 exist."<<std::endl;
  logFile.flush();
#endif
  return false;
}

template< typename T > 
bool AssetManager<T>::DeleteByFilename(char *filename)
{
  typename std::list< AssetHolder<T> >::iterator iter = assetList.begin();
  while(iter!=assetList.end())
    {
      if(strcmp(iter->filename, filename)==0)
	{
#ifdef ASSETLOG
	  logFile<<"II - Unloaded "<<iter->filename<<" by filename."<<std::endl;
	  logFile.flush();
#endif
	  delete iter->assetPtr;
	  assetList.erase(iter);
	  return true;
	}
      iter++;
    }
#ifdef ASSETLOG
  logFile<<"WW - Tried to unload an object by filename, but the object doesn't\
 exist. The filename was '"<<filename<<"'"<<std::endl;
  logFile.flush();
#endif
  return false;
}


#endif

